/*
 * @FileName: DataPointType.ino
 * @Author: Tuya
 * @Email:
 * @LastEditors: shiliu
 * @Date: 2021-04-19 14:31:52
 * @LastEditTime: 2021-11-01 10:02:07
 * @Copyright: HANGZHOU TUYA INFORMATION TECHNOLOGY CO.,LTD
 * @Company: http://www.tuya.com
 * @Description:
 * @Github:https://github.com/tuya/tuya-wifi-mcu-sdk-arduino-library
 */

// serial1 接 TuYa Wifi 模块 TX:19 RX:18
// serial(USB) 接 Log 输出
// 安装 ESP32 by Espressif Systems 2.0.11 开发板
// 安装 TuYa-Wifi 0.0.3 库
// 所有的DP点操作都由 dp_process 函数执行

// Switch1 PIN6
// Switch2 PIN11
// Switch3 PIN8


#include <TuyaWifi.h>

#define DEBUG_KEY 1

// STM32
// HardwareSerial Serial(PA_3, PA_2);

TuyaWifi my_device(&Serial1);

/* Current LED status */
uint8_t led_state = 0;
/* Connect network button pin */
uint8_t wifi_key_pin = 7;

/* Data point define */
#define DPID_SWITCH 1               // 开关(可下发可上报)
#define DPID_PUMP 2                 // 水泵开关(可下发可上报)
#define DPID_LIGHT 3                // 灯光开关(可下发可上报)
#define DPID_LEVEL 4                // 水分等级(可下发可上报)
#define DPID_BRIGHT_VALUE 5         // 灯光亮度(可下发可上报)
#define DPID_TEMP_CURRENT 6         // 当前温度(只上报)
#define DPID_HUMIDITY_CURRENT 7     // 当前湿度(只上报)
#define DPID_PUMP_COUNTDOWN_SET 8   // 倒计时(可下发可上报)
#define DPID_PUMP_COUNTDOWN_LEFT 9  // 倒计时剩余时间(只上报)
#define DPID_FAULT 10               // 故障告警(只上报)

enum WATER_LEVEL {
  LEVEL_1,
  LEVEL_2,
  LEVEL_3,
};

enum COUNT_DOWN {
  COUNT_NULL,
  COUNT_1H,
  COUNT_2H,
  COUNT_3H,
  COUNT_1MIN,
  COUNT_2MIN,
  COUNT_3MIN,
  COUNT_4MIN,
  COUNT_5MIN,
  COUNT_10MIN,
  COUNT_20MIN,
  COUNT_30MIN,
  COUNT_40MIN,
  COUNT_50MIN,
  INVALID_VALUE
};

const char* countDownStrings[] = {
  "NULL",
  "1 H",
  "2 H",
  "3 H",
  "1 MIN",
  "2 MIN",
  "3 MIN",
  "4 MIN",
  "5 MIN",
  "10 MIN",
  "20 MIN",
  "30 MIN",
  "40 MIN",
  "50 MIN",
};

uint8_t pump_pin = 6;
uint8_t switch2_pin = 11;
uint8_t switch3_pin = 8;

#define TURN_ON_PUMP() \
  { \
    digitalWrite(pump_pin, HIGH); \
  }
#define TURN_OFF_PUMP() \
  { \
    digitalWrite(pump_pin, LOW); \
  }

uint32_t switch_max_time_second_set = 10;  //默认的水泵开启最大时间，加上水位传感器后可去除。
uint32_t switch_max_time_mill_set;
/* Current device DP values */
// uint8_t dp_bool_value = 0;
// long dp_value_value = 0;
// uint8_t dp_enum_value = 0;
// uint8_t dp_string_value[8] = {"Hi,Tuya"};
// uint8_t dp_raw_value[8] = {0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef};
// int dp_fault_value = 0x01;

/* Stores all DPs and their types. PS: array[][0]:dpid, array[][1]:dp type.
 *                                     dp type(TuyaDefs.h) : DP_TYPE_RAW, DP_TYPE_BOOL, DP_TYPE_VALUE, DP_TYPE_STRING, DP_TYPE_ENUM, DP_TYPE_BITMAP
 */
uint8_t dp_array[][2] = {
  { DPID_SWITCH, DP_TYPE_BOOL },
  { DPID_PUMP, DP_TYPE_BOOL },
  { DPID_LIGHT, DP_TYPE_BOOL },
  { DPID_LEVEL, DP_TYPE_ENUM },
  { DPID_BRIGHT_VALUE, DP_TYPE_VALUE },
  { DPID_TEMP_CURRENT, DP_TYPE_VALUE },
  { DPID_HUMIDITY_CURRENT, DP_TYPE_VALUE },
  { DPID_PUMP_COUNTDOWN_SET, DP_TYPE_ENUM },
  { DPID_PUMP_COUNTDOWN_LEFT, DP_TYPE_VALUE },
  { DPID_FAULT, DP_TYPE_BITMAP },
};

uint8_t pid[] = { "15lholtup0rjbsmk" };
uint8_t mcu_ver[] = { "1.0.4" };

/* last time */
uint32_t led_last_time = 0;
uint32_t pump_last_time = 0;

bool isPullKey = true;

typedef struct
{
  bool powerSwitch;           // 当前开关
  bool pumpSwitch;            // 当前水泵开关
  bool lightSwitch;           // 当前灯光开关
  WATER_LEVEL level;          // 当前水分等级
  uint16_t brightValue;       // 当前灯光亮度
  uint8_t tempCurrent;        // 当前当前温度
  uint8_t humCurrent;         // 当前当前湿度
  uint8_t pumpCountDownSet;   // 当前水泵倒计时
  uint8_t pumpCountDownLeft;  // 当前水泵倒计时剩余时间
  bool fault;                 // 当前故障告警
} TuYaInfo;
static TuYaInfo info = { 0 };

void setup() {
  Serial.begin(115200);
  Serial1.begin(115200);

  switch_max_time_mill_set = switch_max_time_second_set * 1000;

  // Initialize led port, turn off led.
  pinMode(LED_BUILTIN, OUTPUT);
  digitalWrite(LED_BUILTIN, LOW);
  pinMode(LED_BUILTIN_AUX, OUTPUT);
  digitalWrite(LED_BUILTIN_AUX, LOW);

  // Pump port, turn off pump by low
  pinMode(pump_pin, OUTPUT);
  TURN_OFF_PUMP();

  pinMode(switch2_pin, OUTPUT);
  digitalWrite(switch2_pin, LOW);
  pinMode(switch3_pin, OUTPUT);
  digitalWrite(switch3_pin, LOW);

  // Initialize networking keys.
  pinMode(wifi_key_pin, INPUT_PULLUP);
#if DEBUG_KEY
  //输出低，旁边的CS-KEY方便短接到底，模拟按键按下
  pinMode(10, OUTPUT);
  digitalWrite(10, LOW);
#endif

  // Enter the PID and MCU software version
  my_device.init(pid, mcu_ver);
  // incoming all DPs and their types array, DP numbers
  my_device.set_dp_cmd_total(dp_array, sizeof(dp_array) / 2);
  // register DP download processing callback function
  my_device.dp_process_func_register(dp_process);
  // register upload all DP callback function
  my_device.dp_update_all_func_register(dp_update_all);

  led_last_time = millis();
  pump_last_time = millis();

  Serial.println("TuYA WiFi ESP32C3, Plane Device Demo, by:viking 2023/12/16 \r\n");
}

void loop() {
  my_device.uart_service();

  // Enter the connection network mode when Pin7 is pressed.
  if (digitalRead(wifi_key_pin) == LOW) {
    delay(80);
    if (digitalRead(wifi_key_pin) == LOW && !isPullKey) {
      isPullKey = true;
      my_device.mcu_set_wifi_mode(SMART_CONFIG);
      Serial.println("TuYA WiFi ESP32C3, wifi_key_pin down, start smart config \r\n");
    }
  }
  if (digitalRead(wifi_key_pin) == HIGH) {
    delay(80);
    if (digitalRead(wifi_key_pin) == HIGH) {
      isPullKey = false;
    }
  }

  /* LED blinks when network is being connected */
  if ((my_device.mcu_get_wifi_work_state() != WIFI_LOW_POWER) && (my_device.mcu_get_wifi_work_state() != WIFI_CONN_CLOUD) && (my_device.mcu_get_wifi_work_state() != WIFI_SATE_UNKNOW)) {
    if (millis() - led_last_time >= 500) {
      led_last_time = millis();
      /* Toggle current LED status */
      if (led_state == LOW) {
        led_state = HIGH;
      } else {
        led_state = LOW;
      }

      digitalWrite(LED_BUILTIN, led_state);
    }
  } else {
    digitalWrite(LED_BUILTIN, LOW);
  }

  if (digitalRead(pump_pin) == HIGH)  // 水泵开
  {
    if (info.pumpCountDownSet > COUNT_NULL && info.pumpCountDownSet < INVALID_VALUE) {
      uint32_t switch_millis;
      uint32_t time_mill_set = get_count_down_time_mill(info.pumpCountDownSet);
      uint32_t time_mill_read = millis() - pump_last_time;
      if (time_mill_read >= time_mill_set) {
        Serial.print("pump timeOut Auto off, mills = ");
        Serial.println(time_mill_set);
        Serial.println(info.pumpCountDownSet);
        uint8_t pumpSwitch = 0;
        dp_process(DPID_PUMP, &pumpSwitch, 1);
        delay(100);
        my_device.mcu_dp_update(DPID_PUMP_COUNTDOWN_LEFT, 0, 5);
      }
      //倒计时转换成秒
      uint32_t pumpCountDownLeft = (time_mill_set - time_mill_read) / 1000;
      static uint32_t pumpCountDownLeftLast = pumpCountDownLeft;
      if (pumpCountDownLeft != pumpCountDownLeftLast) {
        if (pumpCountDownLeft % 5 == 0) {
          my_device.mcu_dp_update(DPID_PUMP_COUNTDOWN_LEFT, pumpCountDownLeft, 5);
          Serial.print("pump Count Down Left, second = ");
          Serial.println(pumpCountDownLeft);
        }
      }
      pumpCountDownLeftLast = pumpCountDownLeft;
    }
  } else {
    pump_last_time = millis();
  }

  delay(10);
}

/**
 * @description: DP download callback function.
 * @param {uint8_t} dpid
 * @param {const uint8_t} value
 * @param {unsigned short} length
 * @return {uint8_t}
 */
uint8_t dp_process(uint8_t dpid, const uint8_t value[], unsigned short length) {
  uint32_t pumpCountDownSetSecond;
  switch (dpid) {
    // 当前水泵开关
    case DPID_PUMP:
      info.pumpSwitch = my_device.mcu_get_dp_download_data(dpid, value, length);
      Serial.println("===================");
      Serial.print("Pump is:");
      Serial.println(info.pumpSwitch);
      Serial.println("===================");
      /* After processing the download DP command, the current status should be reported. */
      my_device.mcu_dp_update(dpid, info.pumpSwitch, 1);
      control_pump(info.pumpSwitch);

      if(info.pumpSwitch)
      {
          pumpCountDownSetSecond = get_count_down_time_second(info.pumpCountDownSet);
          my_device.mcu_dp_update(DPID_PUMP_COUNTDOWN_LEFT, pumpCountDownSetSecond, 5);
      }
      else
      {
          my_device.mcu_dp_update(DPID_PUMP_COUNTDOWN_LEFT, 0, 5);
      }
      
      break;
      // case DPID_BOOL:
      //     Serial.println("Bool type:");
      //     dp_bool_value = my_device.mcu_get_dp_download_data(dpid, value, length);
      //     Serial.println(dp_bool_value);
      //     /* After processing the download DP command, the current status should be reported. */
      //     my_device.mcu_dp_update(DPID_BOOL, dp_bool_value, 1);
      //     break;

      // case DPID_VALUE:
      //     Serial.println("Value type:");
      //     dp_value_value = my_device.mcu_get_dp_download_data(DPID_VALUE, value, length);
      //     Serial.println(dp_value_value);
      //     /* After processing the download DP command, the current status should be reported. */
      //     my_device.mcu_dp_update(DPID_VALUE, dp_value_value, 1);
      //     break;

    case DPID_PUMP_COUNTDOWN_SET:
      info.pumpCountDownSet = my_device.mcu_get_dp_download_data(dpid, value, length);
      Serial.println("===================");
      Serial.print("Pump countDown is:");
      Serial.println("Countdown is " + String(countDownStrings[info.pumpCountDownSet]));
      Serial.println("===================");
      /* After processing the download DP command, the current status should be reported. */
      my_device.mcu_dp_update(dpid, info.pumpCountDownSet, 1);
      break;

      // case DPID_STRING:
      //     Serial.println("String type:");
      //     /*  */
      //     for (unsigned int i = 0; i < length; i++)
      //     {
      //         dp_string_value[i] = value[i];
      //         Serial.write(dp_string_value[i]);
      //     }
      //     Serial.println("");
      //     /* After processing the download DP command, the current status should be reported. */
      //     my_device.mcu_dp_update(DPID_STRING, dp_string_value, length);
      //     break;

      // case DPID_RAW:
      //     Serial.println("Raw type:");
      //     /*  */
      //     for (unsigned int i = 0; i < length; i++)
      //     {
      //         dp_raw_value[i] = value[i];
      //         Serial.write(dp_raw_value[i]);
      //     }
      //     Serial.println("");
      //     /* After processing the download DP command, the current status should be reported. */
      //     my_device.mcu_dp_update(DPID_RAW, dp_raw_value, length);
      //     break;

    default:
      break;
  }

  return TY_SUCCESS;
}

void dp_init_all(void) {
}

void control_pump(bool powerOn) {
  if (powerOn) {
    TURN_ON_PUMP();
    digitalWrite(LED_BUILTIN_AUX, HIGH);
  } else {
    TURN_OFF_PUMP();
    digitalWrite(LED_BUILTIN_AUX, LOW);
  }
}

/**
 * @description: Upload all DP status of the current device.
 * @param {*}
 * @return {*}
 */
void dp_update_all(void) {
  Serial.println("===================");
  Serial.println("dp_update_all:");
  Serial.println("===================");
  my_device.mcu_dp_update(DPID_SWITCH, info.powerSwitch, 1);                     // BOOL型数据上报;
  my_device.mcu_dp_update(DPID_PUMP, info.pumpSwitch, 1);                        // BOOL型数据上报;
  my_device.mcu_dp_update(DPID_LIGHT, info.lightSwitch, 1);                      // BOOL型数据上报;
  my_device.mcu_dp_update(DPID_LEVEL, info.level, 1);                            // 枚举型数据上报;
  my_device.mcu_dp_update(DPID_BRIGHT_VALUE, info.brightValue, 2);               // VALUE型数据上报;
  my_device.mcu_dp_update(DPID_TEMP_CURRENT, info.tempCurrent, 1);               // VALUE型数据上报;
  my_device.mcu_dp_update(DPID_HUMIDITY_CURRENT, info.humCurrent, 1);            // VALUE型数据上报;
  my_device.mcu_dp_update(DPID_PUMP_COUNTDOWN_SET, info.pumpCountDownSet, 1);    // 枚举型数据上报;
  my_device.mcu_dp_update(DPID_PUMP_COUNTDOWN_LEFT, info.pumpCountDownLeft, 1);  // VALUE型数据上报;
  my_device.mcu_dp_update(DPID_FAULT, info.fault, 1);                            // 故障型数据上报;
}

uint32_t get_count_down_time_mill(uint8_t enumTime) {
  uint32_t mill = 0;
  switch (enumTime) {
    case COUNT_1H:
      mill = 1 * 60 * 60 * 1000;
      break;
    case COUNT_2H:
      mill = 2 * 60 * 60 * 1000;
      break;
    case COUNT_3H:
      mill = 3 * 60 * 60 * 1000;
      break;
    case COUNT_1MIN:
      mill = 1 * 60 * 1000;
      break;
    case COUNT_2MIN:
      mill = 2 * 60 * 1000;
      break;
    case COUNT_3MIN:
      mill = 3 * 60 * 1000;
      break;
    case COUNT_4MIN:
      mill = 4 * 60 * 1000;
      break;
    case COUNT_5MIN:
      mill = 5 * 60 * 1000;
      break;
    case COUNT_10MIN:
      mill = 10 * 60 * 1000;
      break;
    case COUNT_20MIN:
      mill = 20 * 60 * 1000;
      break;
    case COUNT_30MIN:
      mill = 30 * 60 * 1000;
      break;
    case COUNT_40MIN:
      mill = 40 * 60 * 1000;
      break;
    case COUNT_50MIN:
      mill = 50 * 60 * 1000;
      break;
    default:
      // Handle invalid enum
      break;
  }
  return mill;
}

uint32_t get_count_down_time_second(uint8_t timeMill)
{
  return get_count_down_time_mill(timeMill) / 1000;
}


